﻿using Microsoft.EntityFrameworkCore;
using PJ.IRepository;
using PJ.Model;
using PJ.Repository.DBModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace PJ.Repository
{
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, new()
    {
        protected readonly zlwebContext _dbContext;
        protected readonly DbSet<TEntity> _dbSet;

        public Repository(zlwebContext dbContext)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _dbSet = _dbContext.Set<TEntity>();
        }

        public async Task<int> Add(TEntity model)
        {
            _dbSet.Add(model);
            return await _dbContext.SaveChangesAsync();
        }

        public async Task<int> Add(List<TEntity> models)
        {
            _dbSet.AddRange(models);
            return await _dbContext.SaveChangesAsync();
        }

        public async Task<int> Count(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await (whereExpression != null ? _dbSet.Where(whereExpression).CountAsync() : _dbSet.CountAsync());
        }

        public Task<bool> Delete(object id)
        {
            throw new NotImplementedException();
        }

        public async Task<bool> Delete(Expression<Func<TEntity, bool>> whereExpression)
        {
            List<TEntity> delData = _dbSet.Where(whereExpression).ToList();
            delData.ForEach(model =>
            {
                this._dbSet.Attach(model);
                this._dbSet.Remove(model);
            });
            return await _dbContext.SaveChangesAsync() > 0 ? true : false;
        }

        public async Task<List<TEntity>> Query()
        {
            return await _dbSet.ToListAsync();
        }

        public async Task<List<TEntity>> Query(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await _dbSet.Where(whereExpression).AsNoTracking().ToListAsync();
        }

        public async Task<List<TEntity>> Query(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true)
        {
            if (isAsc)
                return await _dbSet.Where(whereExpression).OrderBy(orderByExpression).AsNoTracking().ToListAsync();
            else
                return await _dbSet.Where(whereExpression).OrderByDescending(orderByExpression).AsNoTracking().ToListAsync();
        }

        public void Update(TEntity model)
        {
            _dbSet.Update(model);
        }

        public async Task<bool> Update(TEntity entity, params string[] updateColumnNames)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));

            if (updateColumnNames != null && updateColumnNames.Any())
            {
                var dbEntity = _dbContext.Entry(entity);
                foreach (var columnName in updateColumnNames)
                {
                    foreach (var property in dbEntity.OriginalValues.Properties)
                    {
                        if (property.Name.Equals(columnName, StringComparison.CurrentCultureIgnoreCase))
                        {
                            dbEntity.Property(property.Name).IsModified = true;
                            break;
                        }
                    }
                }
            }
            return await _dbContext.SaveChangesAsync() > 0 ? true : false;
        }

        public async Task<bool> Exist(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await _dbSet.AnyAsync(whereExpression);
        }

        public async Task<PageModel<TEntity>> QueryPage(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true, int pageIndex = 1, int pageSize = 20)
        {
            if (pageIndex <= 0 || pageSize <= 0) throw new Exception("pageIndex或pageSize不能小于等于0");
            var page = new PageModel<TEntity> { PageIndex = pageIndex, PageSize = pageSize };
            var skip = (pageIndex - 1) * pageSize;
            var able = _dbSet.AsQueryable().AsNoTracking();
            if (whereExpression == null)
            {
                var count = await _dbSet.CountAsync();
                var query = isAsc
                    ? able.OrderBy(orderByExpression).Skip(skip).Take(pageSize)
                    : able.OrderByDescending(orderByExpression).Skip(skip).Take(pageSize);
                page.TotalRows = count;
                page.Data = await query.ToListAsync();
                page.TotalPages = page.TotalRows / pageSize;
                if (page.TotalRows % pageSize != 0) page.TotalPages++;
            }
            else
            {
                var queryable = able.Where(whereExpression);
                var count = await queryable.CountAsync();
                var query = isAsc
                    ? queryable.OrderBy(orderByExpression).Skip(skip).Take(pageSize)
                    : queryable.OrderByDescending(orderByExpression).Skip(skip).Take(pageSize);
                page.TotalRows = count;
                page.Data = await query.ToListAsync();
                page.TotalPages = page.TotalRows / pageSize;
                if (page.TotalRows % pageSize != 0) page.TotalPages++;
            }
            return page;
        }
    }
}
